﻿namespace LogManager.Core.Services.Location
{
    using LogManager.Core.Helpers;
    using LogManager.Core.Models;
    using LogManager.Utils.Authentification;
    using LogManager.Utils.IO;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Threading;
    using System.Threading.Tasks;

    public class LocationService : ILocationService
    {
        public static SimpleServiceResult CheckLocationSource(ILocation location, IProgress<LocationCheckProgress> locationProgress)
        {
            locationProgress.Report(new LocationCheckProgress(location, null, LocationCheckPath.Source, OperationState.Starting));
            var sourceResult = CheckLocationSource(location);
            locationProgress.Report(new LocationCheckProgress(location, sourceResult, LocationCheckPath.Source, OperationState.Ended));
            return sourceResult;
        }

        public static SimpleServiceResult CheckLocationSource(ILocation location)
        {
            try
            {
                using (var imp = new Impersonator(location.GetSourceCredentials()))
                {
                    PathHelper.CheckFullAccessDirectory(location.FullPathSource);
                }
                return SimpleServiceResult.CreateSuccess("Ok");
            }
            catch (Exception ex)
            {
                return SimpleServiceResult.CreateFailure(ex.Message, ex);
            }
        }

        public static SimpleServiceResult CheckLocationDestination(ILocation location, IProgress<LocationCheckProgress> locationProgress)
        {
            locationProgress.Report(new LocationCheckProgress(location, null, LocationCheckPath.Destination, OperationState.Starting));
            var destResult = CheckLocationDestination(location);
            locationProgress.Report(new LocationCheckProgress(location, destResult, LocationCheckPath.Destination, OperationState.Ended));
            return destResult;
        }

        public static SimpleServiceResult CheckLocationDestination(ILocation location)
        {
            try
            {
                using (var imp = new Impersonator(location.GetDestinationCredentials()))
                {
                    Directory.CreateDirectory(location.FullPathDestination);
                    PathHelper.CheckFullAccessDirectory(location.FullPathDestination);
                }
                return SimpleServiceResult.CreateSuccess("Ok");
            }
            catch (Exception ex)
            {
                return SimpleServiceResult.CreateFailure(ex.Message, ex);
            }
        }

        public static LogFilesSearchResult FindEligibleFiles(ILocation location, IProgress<LocationFilesSearchProgress> filesSearchProgress)
        {
            filesSearchProgress.Report(new LocationFilesSearchProgress(location, null, OperationState.Starting));
            var filesResult = FindEligibleFiles(location);
            filesSearchProgress.Report(new LocationFilesSearchProgress(location, filesResult, OperationState.Ended));
            return filesResult;
        }

        public static LogFilesSearchResult FindEligibleFiles(ILocation location)
        {
            try
            {
                return LogFilesSearchResult.CreateSuccess(LogFile.GetFiles(location), "ok");
            }
            catch (Exception ex)
            {
                return LogFilesSearchResult.CreateFailure("File search Failed " + ex.Message, ex);
            }
        }

        public static void CheckLocationsAccess(
            IConfiguration configuration,
            CancellationToken ct,
            IProgress<LocationArchivingProgress> LocationArchivingProgressHandler,
            IProgress<object> ItemFinished,
            bool searchFiles)
        {
            var stepsExecuter = new ArchivingStepsExecuter(LocationArchivingProgressHandler);
            foreach (var warehouse in configuration.Warehouses)
            {
                foreach (var plateform in warehouse.Plateforms)
                {
                    foreach (var server in plateform.Servers)
                    {
                        foreach (var location in server.Locations)
                        {
                            var archivingProgress = new LocationArchivingProgress(location);
                            stepsExecuter.ProgressValue = archivingProgress;

                            stepsExecuter.ExecuteArchivingStep(LocationArchivingStep.CheckSource, () => { archivingProgress.SourceResult = CheckLocationSource(location); });
                            ct.ThrowIfCancellationRequested();

                            stepsExecuter.ExecuteArchivingStep(LocationArchivingStep.CheckDestination, () => { archivingProgress.DestinationResult = CheckLocationDestination(location); });
                            ct.ThrowIfCancellationRequested();

                            if (archivingProgress.SourceResult.Succeeded && searchFiles)
                            {
                                stepsExecuter.ExecuteArchivingStep(LocationArchivingStep.CheckFiles, () => { archivingProgress.LogFilesSearchResult = FindEligibleFiles(location); });
                            }

                            ItemFinished.Report(location);
                            ct.ThrowIfCancellationRequested();
                        }
                        ItemFinished.Report(server);
                    }
                    ItemFinished.Report(plateform);
                }
                ItemFinished.Report(warehouse);
            }
        }

        public static LogArchivingResult LaunchArchiving(
            IConfiguration configuration,
            CancellationToken ct,
            IProgress<LocationArchivingProgress> LocationArchivingProgressHandler,
            IProgress<object> ItemFinished,
            IProgress<LogFileArchivingProgress> logFileProgressHandler)
        {
            var result = new List<LocationArchivingProgress>();
            var stepsExecuter = new ArchivingStepsExecuter(LocationArchivingProgressHandler);
            foreach (var warehouse in configuration.Warehouses)
            {
                foreach (var plateform in warehouse.Plateforms)
                {
                    foreach (var server in plateform.Servers)
                    {
                        foreach (var location in server.Locations)
                        {
                            var archivingProgress = new LocationArchivingProgress(location);
                            result.Add(archivingProgress);
                            stepsExecuter.ProgressValue = archivingProgress;

                            stepsExecuter.ExecuteArchivingStep(LocationArchivingStep.CheckSource, () => { archivingProgress.SourceResult = CheckLocationSource(location); });
                            ct.ThrowIfCancellationRequested();

                            stepsExecuter.ExecuteArchivingStep(LocationArchivingStep.CheckDestination, () => { archivingProgress.DestinationResult = CheckLocationDestination(location); });
                            ct.ThrowIfCancellationRequested();

                            if (archivingProgress.SourceResult.Succeeded && archivingProgress.DestinationResult.Succeeded)
                            {
                                stepsExecuter.ExecuteArchivingStep(LocationArchivingStep.CheckFiles, () => { archivingProgress.LogFilesSearchResult = FindEligibleFiles(location); });

                                if (archivingProgress.LogFilesSearchResult.Succeeded)
                                {
                                    stepsExecuter.ExecuteArchivingStep(
                                       LocationArchivingStep.ArchiveFiles,
                                       () =>
                                       {
                                           foreach (var logFile in archivingProgress.LogFilesSearchResult.Files)
                                           {
                                               logFile.Archive(logFileProgressHandler);
                                               ct.ThrowIfCancellationRequested();
                                           }
                                       });
                                }
                            }

                            ItemFinished.Report(location);
                            ct.ThrowIfCancellationRequested();
                        }
                        ItemFinished.Report(server);
                    }
                    ItemFinished.Report(plateform);
                }
                ItemFinished.Report(warehouse);
            }

            return new LogArchivingResult(configuration, result);
        }

        public async Task<SimpleServiceResult> CheckLocationSourceAsync(ILocation location)
        {
            return await Task.Factory.StartNew<SimpleServiceResult>(() => CheckLocationSource(location)).ConfigureAwait(false);
        }

        public async Task<SimpleServiceResult> CheckLocationDestinationAsync(ILocation location)
        {
            return await Task.Factory.StartNew<SimpleServiceResult>(() => CheckLocationDestination(location)).ConfigureAwait(false);
        }

        public async Task CheckLocationsAccessAsync(
            IConfiguration configuration,
            CancellationToken ct,
            IProgress<LocationArchivingProgress> LocationArchivingProgressHandler,
            IProgress<object> ItemFinished,
            bool searchFiles)
        {
            await Task.Factory.StartNew(() => CheckLocationsAccess(configuration, ct, LocationArchivingProgressHandler, ItemFinished, searchFiles)).ConfigureAwait(false);
        }

        public async Task<LogArchivingResult> LaunchArchivingAsync(
            IConfiguration configuration,
            CancellationToken ct,
           IProgress<LocationArchivingProgress> LocationArchivingProgressHandler,
            IProgress<object> ItemFinished,
            IProgress<LogFileArchivingProgress> logFileProgress)
        {
            return await Task.Factory.StartNew<LogArchivingResult>(() => LaunchArchiving(configuration, ct, LocationArchivingProgressHandler, ItemFinished, logFileProgress)).ConfigureAwait(false);
        }

        private class ArchivingStepsExecuter
        {
            public ArchivingStepsExecuter(IProgress<LocationArchivingProgress> progressHandler)
            {
                this.ProgressHandler = progressHandler;
            }

            public IProgress<LocationArchivingProgress> ProgressHandler { get; private set; }
            public LocationArchivingProgress ProgressValue { get; set; }

            public void ExecuteArchivingStep(LocationArchivingStep locationArchivingStep, Action action)
            {

                ProgressValue.LocationArchivingStep = locationArchivingStep;
                ProgressValue.OperationState = OperationState.Starting;
                ProgressHandler.Report(new LocationArchivingProgress(ProgressValue));
                Thread.Sleep(500);
                action();
                ProgressValue.OperationState = OperationState.Ended;
                ProgressHandler.Report(new LocationArchivingProgress(ProgressValue));
            }
        }
    }
}
